
// Copyright (C) 2012 Luca Piccioni
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//   
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Schema;

using Derm.Xml;

namespace Derm.Render
{
	/// <summary>
	/// A collection of resources.
	/// </summary>
	public class RenderResourceDb : IXmlSerializableExt
	{
		#region Resource Database

		/// <summary>
		/// Add a resource to this resource database.
		/// </summary>
		/// <param name="renderResource">
		/// A <see cref="IRenderResource"/> to be added to this resoure database.
		/// </param>
		public void AddResource(IRenderResource renderResource)
		{
			if (renderResource == null)
				throw new ArgumentNullException("renderResource");
			if (HasResource(renderResource.Identifier))
				throw new ArgumentException(String.Format("resource identifier {0} would be duplicated", renderResource.Identifier), "renderResource");

			mResourceDb[renderResource.Identifier] = renderResource;
		}

		/// <summary>
		/// Remove a resource from this resource database.
		/// </summary>
		/// <param name="renderResource">
		/// A <see cref="System.String"/> that identifies the resource to be removed from this resource database.
		/// </param>
		public void RemoveResource(string resourceIdentifier)
		{
			if (resourceIdentifier == null)
				throw new ArgumentNullException("resourceIdentifier");
			if (HasResource(resourceIdentifier) == false)
				throw new ArgumentException(String.Format("resource identifier {0} doesn't exists in this database", resourceIdentifier), "resourceIdentifier");

			mResourceDb.Remove(resourceIdentifier);
		}

		/// <summary>
		/// Determine whether a resource is currently present on this resource database.
		/// </summary>
		/// <param name="resourceId">
		/// A <see cref="System.String"/> that identifies the resource.
		/// </param>
		/// <returns>
		/// It returns a boolean value indicating whether the resource identified by <paramref name="resourceId"/>
		/// is actually present on this resource database.
		/// </returns>
		public bool HasResource(string resourceId)
		{
			if (resourceId == null)
				throw new ArgumentNullException("resourceId");

			return (mResourceDb.ContainsKey(resourceId));
		}

		/// <summary>
		/// Get a resource collected by this resource database.
		/// </summary>
		/// <param name="resourceId">
		/// A <see cref="System.String"/> that identifies the resource.
		/// </param>
		/// <returns>
		/// It returns the resource identified by <paramref name="resourceId"/>, or null in the case there's no
		/// resource with the specified identifier.
		/// </returns>
		public IRenderResource GetResource(string resourceId)
		{
			IRenderResource renderResource;

			if (resourceId == null)
				throw new ArgumentNullException("resourceId");

			if (mResourceDb.TryGetValue(resourceId, out renderResource) == false)
				return (null);

			return (renderResource);
		}

		/// <summary>
		/// The resource database.
		/// </summary>
		/// <remarks>
		/// Each resource is mapped uniquely using a string. The string shall be the resource identifier.
		/// </remarks>
		private readonly Dictionary<string, IRenderResource> mResourceDb = new Dictionary<string, IRenderResource>();

		#endregion

		#region Resource Database Methods

		/// <summary>
		/// Merge a resource database with this resource database.
		/// </summary>
		/// <param name="resourceDb">
		/// A <see cref="RenderResourceDb"/> to be merged with this resource database.
		/// </param>
		/// <exception cref="InvalidOperationException">
		/// Exception thrown if any resource collected in <paramref name="resourceDb"/> would conflict with
		/// resource identifiers collected in this resource database.
		/// </exception>
		public void Merge(RenderResourceDb resourceDb)
		{
			if (resourceDb == null)
				throw new ArgumentNullException("resourceDb");

			// Check for duplicated identifiers
			foreach (KeyValuePair<string, IRenderResource> pair in resourceDb.mResourceDb) {
				if (mResourceDb.ContainsKey(pair.Key) == true)
					throw new InvalidOperationException(String.Format("resource identifier {0} would be duplicated", pair.Key));
			}

			// No duplicates: merge databases
			foreach (KeyValuePair<string, IRenderResource> pair in resourceDb.mResourceDb)
				mResourceDb.Add(pair.Key, pair.Value);
		}

		#endregion

		#region Global Resource Database

		/// <summary>
		/// Global resource database.
		/// </summary>
		/// <remarks>
		/// <para>
		/// The global resource database is used by application to support complex resource reference systems.
		/// </para>
		/// <para>
		/// Many classes allow local resource databases, but often they are an alias to the global database. This allow
		/// to specify a resource locally and globally.
		/// </para>
		/// </remarks>
		public static RenderResourceDb GlobalDb { get { return (sGlobalResourceDb); } }

		/// <summary>
		/// Global resource database.
		/// </summary>
		private static readonly RenderResourceDb sGlobalResourceDb = new RenderResourceDb();

		#endregion

		#region IXmlSerializableExt Implementation

		/// <summary>
		/// This method is reserved (instead use System.Xml.Serialization.XmlSchemaProviderAttribute to the class instead).
		/// </summary>
		/// <returns>
		/// It always returns null.
		/// </returns>
		public XmlSchema GetSchema() { return (null); }

		/// <summary>
		/// Generates an object from its XML representation.
		/// </summary>
		/// <param name="xmlReader">
		/// The System.Xml.XmlReader stream from which the object is deserialized.
		/// </param>
		public void ReadXml(XmlReader xmlReader)
		{
			XmlHelper.ReadXml(xmlReader, this);
		}

		/// <summary>
		/// Converts an object into its XML representation.
		/// </summary>
		/// <param name="writer">
		/// The System.Xml.XmlWriter stream to which the object is serialized.
		/// </param>
		public void WriteXml(XmlWriter writer)
		{
			
		}

		/// <summary>
		/// Read the attribute of the current XML serializable.
		/// </summary>
		/// <param name="xmlReader">
		/// A <see cref="XmlReader"/> used for reading element attributes.
		/// </param>
		public void ReadAttributes(XmlReader xmlReader)
		{
			if (xmlReader == null)
				throw new ArgumentNullException("xmlReader");


		}

		/// <summary>
		/// Read an element of the current XML serializable.
		/// </summary>
		/// <param name="elementName">
		/// A <see cref="System.String"/> that specify the element name.
		/// </param>
		/// <param name="xmlReader">
		/// A <see cref="XmlReader"/> used for reading child element.
		/// </param>
		public void ReadElement(string elementName, XmlReader xmlReader)
		{
			IRenderResource renderResource = null;
			bool isEmpty;

			if (elementName == null)
				throw new ArgumentNullException("elementName");
			if (xmlReader == null)
				throw new ArgumentNullException("xmlReader");

			if (elementName == XmlNameTexture2dResource) {
				// Create resource
				renderResource = new Texture2d();
			} else if (elementName == XmlNameTextureRectResource) {
				// Create resource
				renderResource = new TextureRectangle();
			} else
				xmlReader.Skip();

			// Resource loading
			if (renderResource != null) {
				// Decode resource
				XmlHelper.ReadXml(xmlReader, renderResource);
				// Include resource into this database
				AddResource(renderResource);
			}
		}

		/// <summary>
		/// Read the text of the current XML serializable.
		/// </summary>
		/// <param name="xmlReader">
		/// A <see cref="XmlReader"/> used for reading child element. It is positioned at the start element.
		/// </param>
		public void ReadText(XmlReader xmlReader)
		{
			
		}

		/// <summary>
		/// Name of the element that specify a texture resource.
		/// </summary>
		private const string XmlNameTexture2dResource = "Texture2d";

		/// <summary>
		/// Name of the element that specify a texture resource.
		/// </summary>
		private const string XmlNameTextureRectResource = "TextureRect";

		#endregion
	}
}
